import torch

"""
PyTorch: Tensors

Numpy 是一个伟大的框架，但是他不能利用 GPUs 来加快数值计算。对于现代深度神经网络， GPU 通常提供50倍或更高的加速，所以不幸的是 numpy 不足以用于现代深度学习。
这里，我介绍 PyTorch 中最基础的概念： 张量（Tensor）。Tensor 在概念上与 numpy 数组相同，它是一个 N 维的数组，并提供很多操作它的函数。
和 numpy 类似，PyTorch 中的 Tensors 没有深度学习、计算图和梯度这些内容，他们是科学计算的通用工具。
然而，PyTorch的Tensor 和 numpy 不同，它可以利用 GPUs 来加快数值计算。为了让 Tensors 在 GPU上运行，你只需把它转型维一个新的类型即可。
这里我们使用 PyTorch 来训练一个两层的网络让它拟合随机数据。 和上面 numpy 的例子类似，我们需要手动实现网络的前向和反向传播：
"""

# CPU数据类型
dtype = torch.FloatTensor
# GPU数据类型
# dtype = torch.cuda.FloatTensor # Uncomment this to run GPU

# N 批的大小
# D_in 输入的数据纬度
# H 隐藏层纬度
# D_out 输出数据纬度
# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 1, 2, 2, 1

# 创建随机的输入和输出数据纬度
# create random input and output data
x = torch.randn(N, D_in).type(dtype)
y = torch.randn(N, D_out).type(dtype)

# 随机初始化权重w的数据，权重在0左右之间，呈正态分布
# Randomly initialize weights
w1 = torch.randn(D_in, H).type(dtype)
w2 = torch.randn(H, D_out).type(dtype)

# 设置学习率
learning_rate = 1e-2

# 开始学习
for t in range(10):
    # 正向传播: 计算预测值 y
    # Forward pss: compute predicted y
    # tensor.mm是矩阵乘积
    h = x.mm(w1)
    # 将input中的元素限制在[min,max]范围内并返回一个Tensor
    h_relu = h.clamp(min=0)
    y_pred = h_relu.mm(w2)

    # 计算和打印损失
    # compute and print loss
    loss = (y_pred - y).pow(2).sum()
    print(t, loss)

    # 反向传播 根据损失计算w1和w2的梯度
    # backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 3.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h<0] = 0
    grad_w1 = x.t().mm(grad_h)

    # 使用梯度下降法更新权重
    # update weights using gradient descent
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2